package cn.geminis.lowcode.processor;

import cn.geminis.lowcode.annotation.CrudController;
import cn.geminis.lowcode.compiler.code.*;
import cn.geminis.lowcode.utils.Flags;
import cn.geminis.lowcode.utils.Utils;

import javax.lang.model.element.Element;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;

/**
 * @author puddi
 */
public class CrudControllerProcessor extends AbstractControllerProcessor {

    @Override
    public Class<? extends Annotation> getSupportAnnotation() {
        return CrudController.class;
    }

    @Override
    public void addContent(LcClass controller, Element element) {
        var annotation = element.getAnnotation(CrudController.class);
        var entityQualifiedName = Utils.getElementQualifiedName(element);
        var entityPackageName = Utils.getElementPackageName(element);
        var filterName = entityPackageName + ".filter." + element.getSimpleName() + "Filter";
        var repositoryName = entityPackageName + ".repository." + element.getSimpleName() + "Repository";

        if (annotation.delete()) {
            addDeleteMethod(controller, element, annotation.deleteInBatch());
        }
        if (annotation.save()) {
            addSaveMethod(controller, entityQualifiedName);
        }
        if (annotation.update()) {
            addUpdateMethod(controller, entityQualifiedName);
        }
        if (annotation.findOne()) {
            addFindOneMethod(controller, entityQualifiedName, filterName);
        }
        if (annotation.findPage()) {
            addFindPageMethod(controller, entityQualifiedName, filterName);
        }
        if (annotation.repository()) {
            addRepositoryField(controller, repositoryName);
        }
    }

    public void addFindPageMethod(LcClass controller, String entityName, String filterName) {
        var method = new LcMethod(
                new LcModifier(
                        Flags.PUBLIC,
                        new LcAnnotation("org.springframework.web.bind.annotation.GetMapping")
                ),
                "org.springframework.data.domain.Page<" + entityName + ">",
                "findPage",
                List.of(
                        new LcVariable(filterName, "queryFilter"),
                        new LcVariable("cn.geminis.data.jpa.PageQuery", "pageQuery")
                ),
                List.of(),
                List.of(
                        new LcApply(
                                "this.repository.findAll",
                                new LcIdent("queryFilter"),
                                new LcApply(
                                        "pageQuery.toPageable"
                                )
                        ).rtn()
                )
        );
        controller.prepend(method);
    }

    public void addFindOneMethod(LcClass controller, String entityName, String filterName) {
        var method = new LcMethod(
                new LcModifier(
                        Flags.PUBLIC,
                        new LcAnnotation(
                                "org.springframework.web.bind.annotation.GetMapping",
                                Map.of("", "one")
                        )
                ),
                entityName,
                "findOne",
                List.of(
                        new LcVariable(filterName, "queryFilter")
                ),
                List.of(),
                List.of(
                        new LcApply(
                                List.of(
                                        new LcApply.InvokeInfo(
                                                "this.repository.findOne",
                                                new LcIdent("queryFilter")
                                        ),
                                        new LcApply.InvokeInfo("get")
                                )
                        ).rtn()
                )
        );
        controller.prepend(method);
    }

    private void addSaveMethod(LcClass controller, String entityName) {
        var method = new LcMethod(
                new LcModifier(
                        Flags.PUBLIC,
                        new LcAnnotation("org.springframework.web.bind.annotation.PostMapping")
                ),
                "VOID",
                "save",
                List.of(
                        new LcVariable(
                                new LcModifier(Flags.PARAMETER, new LcAnnotation("org.springframework.web.bind.annotation.RequestBody")),
                                entityName,
                                "obj"
                        )
                ),
                List.of(),
                List.of(
                        new LcApply("this.repository.save", new LcIdent("obj")).exec()
                )
        );
        controller.prepend(method);
    }

    private void addUpdateMethod(LcClass controller, String entityName) {
        var method = new LcMethod(
                new LcModifier(
                        Flags.PUBLIC,
                        new LcAnnotation("org.springframework.web.bind.annotation.PutMapping")
                ),
                "VOID",
                "update",
                List.of(
                        new LcVariable(
                                new LcModifier(Flags.PARAMETER, new LcAnnotation("org.springframework.web.bind.annotation.RequestBody")),
                                entityName,
                                "obj"
                        )
                ),
                List.of(),
                List.of(
                        new LcApply("this.repository.update", new LcIdent("obj")).exec()
                )
        );
        controller.prepend(method);
    }

    public void addDeleteMethod(LcClass controller, Element element, boolean inBatch) {
        var entityClass = new LcClass(element);

        var method = new LcMethod(
                new LcModifier(
                        Flags.PUBLIC,
                        new LcAnnotation("org.springframework.web.bind.annotation.DeleteMapping")
                ),
                "VOID",
                "delete",
                List.of(
                        new LcVariable(
                                new LcModifier(Flags.PARAMETER, new LcAnnotation("org.springframework.web.bind.annotation.RequestBody")),
                                "java.util.List<java.lang.String>",
                                "ids"
                        )
                ),
                List.of(),
                List.of(
                        new LcApply(
                                entityClass.existAnnotation("cn.geminis.lowcode.annotation.SoftDeleteEntity") ?
                                        "this.repository.softDelete" :
                                        inBatch ? "this.repository.deleteAllByIdInBatch" : "this.repository.deleteAllById",
                                new LcIdent("ids")
                        ).exec()
                )
        );
        controller.prepend(method);
    }

    private void addRepositoryField(LcClass controller, String repositoryType) {
        var repositoryField = new LcVariable(
                new LcModifier(Flags.PRIVATE | Flags.FINAL),
                repositoryType,
                "repository"
        );
        controller.prepend(repositoryField);
    }
}
